//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace OpenLS.Spreadsheet.Internals
{
    abstract public class ChildGroup: Group
    {

        internal ChildGroup(Group parent, Dimension first, Dimension last)
        {
            Contract.Requires(first.Index < last.Index);
            _first = first;
            _last = last;
            foreach (Dimension dimension in GetDimensions(false))
                dimension.Group = this;
            _parent = parent;
            First.PropertyChanged += First_PropertyChanged;
            Last.PropertyChanged += Last_PropertyChanged;
            first.Worksheet.DimensionPropertyChanged += DimensionPropertyChanged;
        }

        private void DimensionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            Dimension dim = (Dimension) sender;
            Contract.Assert(dim != null);
            if (dim.Index < First.Index || dim.Index > Last.Index)
                return;
            switch (e.PropertyName)
            {
                case "Offset":
                case "ActualWidth":
                case "ActualHeight":
                    NotifyTotalChanged();
                    break;
            }

        }

        internal override bool Intersects(Dimension first, Dimension next)
        {
            Contract.Assert(first.Index < next.Index);
            if (next.Index < First.Index)
                return false;
            if (first.Index > Last.Index)
                return false;
            return true;
        }
        internal override void Ungroup(Dimension first, Dimension next)
        {
            if (Children.Count == 0)
            {
                UngroupNoChild(first, next);
                return;
            }
            ChildGroup firstGroup = (ChildGroup)Children[0];


            foreach (var group in Children)
            {

            }
        }

        public override bool IsDescendantOf (Group ancestor)
        {
            if (Parent == ancestor)
                return true;
            var c = Parent as ChildGroup;
            if (c == null)
                return false;
            return c.IsDescendantOf(ancestor);
        }

        internal void ExpandBounds(ref Dimension first, ref Dimension next)
        {
            if (First.Index < first.Index)
                first = First;
            if (Last.Index > next.Index)
                next = Last;
        }
        internal override Worksheet Worksheet
        {
            get { return Parent.Worksheet; }
        }
        protected override void UngroupNoChild(Dimension first, Dimension last)
        {
            if (first.Index <= First.Index && last.Index >= Last.Index)
            {
                Die();
                return;
            }
            Group parent = Parent;
            Die();
            ChildGroup beforeGroup = null;
            if (First.Index < first.Index)
                beforeGroup = CreateGroup(Parent, First, first, IsExpanded);
            ChildGroup afterGroup = null;
            if (last.Index < Last.Index)
                afterGroup = CreateGroup(Parent, last, Last, IsExpanded);
            if (beforeGroup != null)
                parent.AddChild(beforeGroup);
            if (afterGroup != null)
                parent.AddChild(afterGroup);
        }

        private ChildGroup CreateGroup(Group parent, Dimension first, Dimension next, bool isExpanded)
        {
            var result = CreateGroup(parent, first, next);
            result.IsExpanded = isExpanded;
            return result;
        }

        public override sealed ChildGroup CreateGroup (Group parent, Dimension first, Dimension next)
        {
            return Root.CreateGroup(parent, first, next);
        }
        public override int Depth
        {
            get { return Parent.Depth + 1; }
        }
        internal override Group Root
        {
            get { return Parent.Root; }
        }

        void Last_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "Offset":
                case "ActualWidth":
                case "ActualHeight":
                    NotifyTotalChanged();
                    break;
            }
        }

        protected abstract void NotifyTotalChanged();

        void First_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Offset")
                NotifyPropertyChanged("FirstOffset");
        }

        internal override bool Contains(Dimension dimension)
        {
            return dimension.Index >= First.Index && dimension.Index <= Last.Index;
        }
        public double FirstOffset{get{ return First.Offset;}}

        public bool IsMeaningful
        {
            get
            {
                var p = Parent as ChildGroup;
                if (p == null)
                    return true;
                return !(p.Last == Last);
            }
        }
        
        
        private bool _isExpanded;
        public bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                if (_isExpanded == value)
                    return;
                _isExpanded = value;
                foreach (Dimension dimension in GetDimensions(false))
                {
                    dimension.ApplyGroupVisibility();
                }
                double dummy = Last.Offset;
                NotifyPropertyChanged("IsExpanded");
                NotifyTotalChanged();
                TopParent.OnChildExpandedChanged();
            }
        }

        internal bool IsVisibleAndExpanded
        {
            get
            {
                if (!IsExpanded)
                    return false;
                var group =  Parent as ChildGroup;
                if (group == null)
                    return true;
                return group.IsVisibleAndExpanded;
            }
        }
        internal void Die()
        {
            First.PropertyChanged -= First_PropertyChanged;
            Last.PropertyChanged -= Last_PropertyChanged;
            First.Worksheet.DimensionPropertyChanged -= this.DimensionPropertyChanged;
            var newGroup = Parent as ChildGroup;
            foreach (Dimension dimension in GetDimensions(true))
            {
                dimension.Group = newGroup;
            }
            Parent.RemoveChild(this);
#if DEBUG
            isDead = true;
#endif
            
        }


        internal abstract IEnumerable<Dimension> GetDimensions(bool includeNext);

        internal override Group Parent
        {
            get { return _parent; }
        }
#if DEBUG
        private bool isDead;
#endif
        private readonly Group _parent;
        private readonly Dimension _first;
        private readonly Dimension _last;
        public Dimension First
        {
            get { return _first; }
           /* set
            {
                _first = value; NotifyPropertyChanged("First");
            }*/
        }
        public Dimension Last
        {
            get { return _last; }
           /* set
            {
                _next = value; NotifyPropertyChanged("Next");
            }*/
        }

        internal static ChildGroup Create<T> (Group parent, T first, T next) 
        {
            if (typeof(T) == typeof(Row))
                return RowGroup.CreateColumnGroupWithNext(parent, (Row)(object)first, (Row)(object)next);
            return ColumnGroup.CreateColumnGroupWithNext(parent, (Column)(object)first, (Column)(object)next);
        }

        internal  void OnDimensionDeleted(Dimension dimension)
        {
            Dimension newFirst = null;
            if (First == dimension)
            {
                newFirst = GetDimension(First.Index + 1);
            }
            Dimension newNext = null;
            if (Last == dimension)
            {
                newNext = GetDimension(dimension.Index - 1);
            }
            if (newFirst != null || newNext != null)
            {
                Group parent = Parent;
                Die();
                if (newFirst != newNext)
                   parent.AddChild(CreateGroup(parent, newFirst, newNext, IsExpanded));
                if (parent is ChildGroup)
                    ((ChildGroup)parent).OnDimensionDeleted(dimension);
            }
        }

        protected abstract Dimension GetDimension(int i);


        internal void TransferTo(ChildGroup newParentGroup)
        {
            Dimension first = First;
            Dimension next = Last;
            if (first.Index < newParentGroup.First.Index)
                first = newParentGroup.First;
            if (next.Index > newParentGroup.Last.Index)
                next = newParentGroup.Last;
            ChildGroup newChildGroup = newParentGroup.CreateGroup(newParentGroup, first, next);
            newChildGroup.IsExpanded = this.IsExpanded;
            newParentGroup.AddChild(newChildGroup);
            foreach(ChildGroup g in Children)
                g.TransferTo(newChildGroup);
        }

    }
}